/**
 * @author daniele.gili@usi.ch
 * @author blancsv@usi.ch
 */

package ch.usi.inf.pf2;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Timer;

import javax.swing.JComponent;

public class PanicSimDrawingArea extends JComponent implements
		java.io.Serializable {

	private final PanicSimMain circuit;
	private CommonGate commonGateFinder;

	private ArrayList<CommonGate> selectedGate = new ArrayList<CommonGate>();
	private ArrayList<Output> selectedOutput = new ArrayList<Output>();

	private MegaOutput selectedMegaOutput;
	private MegaInput selectedMegaInput;

	private int mouseX;
	private int mouseY;
	private int mouseXDrag;
	private int mouseYDrag;
	private boolean onaElement;
	private Output outputDraw = null;

	private MegaInputDrawer megaInputDrawer;
	private MegaOutputDrawer megaOutputDrawer;
	private boolean onAPin;
	private boolean wireEnable;
	private boolean notEnable;
	private boolean andEnable;
	private boolean orEnable;
	private boolean nandEnable;
	private boolean xorEnable;
	private boolean clockedEnable;

	private boolean megaInputEnable;
	private boolean megaOutputEnable;

	private boolean compileEnable;
	private PanicSimDrawingArea area;
	
	private int n = 0;

	public PanicSimDrawingArea(final PanicSimMain circuit) {
		
		

		setCommonGateFinder(new CommonGate());
		this.circuit = circuit;

		addMouseMotionListener(new MouseAdapter() {
			public void mouseDragged(final MouseEvent ev) {

				if (wireEnable
						&& circuit.getNumberParts() > 0
						&& circuit.getGate(circuit.getNumberParts() - 1) instanceof Wire) {
					circuit.getpartList().remove(circuit.getNumberParts() - 1);
					circuit.addPart(new Wire(mouseX, mouseY, ev.getX(), ev
							.getY()));

					// for every object
					for (int i = 0; i < circuit.getNumberParts(); i++) {

						// if I found a gate
						if (circuit.getGate(i) instanceof CommonGate) {

							// i check its pins
							for (int c = 0; c < ((CommonGate) circuit
									.getGate(i)).getInputs().size(); c++) {

								// if its input contains che dragged
								if (((CommonGate) circuit.getGate(i))
										.getInputs().get(c)
										.contains(ev.getX(), ev.getY())) {

									onAPin = true;
									selectedGate.add((CommonGate) circuit
											.getGate(i));

									// then i check if the gate has an output.
									if (circuit.getGate(i + 1) != null
											&& circuit.getGate(i + 1)
													.getClass()
													.getGenericSuperclass() == Pin.class) {
										selectedOutput.add((Output) circuit
												.getGate(i + 1));
									}

									break;
								}

							}
						}

					}
					if (!onAPin && selectedGate.size() > 0
							&& selectedOutput.size() > 0) {
						for (int i = 0; i < selectedGate.size(); i++) {
							selectedGate.remove(i);
							selectedOutput.remove(i);
						}
					}
					repaint();
					onAPin = false;

				}

				if (onaElement && selectedGate.size() > 0) {
					// for every part of the circuit

					for (int i = 0; i < circuit.getNumberParts(); i++) {
						for (int f = 0; f < selectedGate.size(); f++) {
							// if it is a gate...
							if (circuit.getGate(i) instanceof CommonGate
									&& selectedGate.get(f).getIndex() == i) {
								((CommonGate) circuit.getGate(i))
										.setNewAttributes(ev.getX()
												- mouseXDrag,
										// - selectedGate.get(f).width
										// / 2,
												ev.getY() - mouseYDrag);
								// - (selectedGate.get(f).height / 2));

								ArrayList<Input> inputs = ((CommonGate) circuit
										.getGate(i)).getInputs();

								for (int c = 0; c < inputs.size(); c++) {
									inputs.get(c).setNewAttributes(
											ev.getX() - mouseXDrag,
											ev.getY() - mouseYDrag,
											inputs.size(), c);
									if (inputs.get(c).getInput() != null) {
										inputs.get(c).getInput().setNewEnd();
									}

								}

								if (circuit.getGate(i).getClass() != MegaOutput.class) {
									((Output) circuit.getGate(i + 1))
											.setNewAttributes(ev.getX()
													- mouseXDrag, ev.getY()
													- mouseYDrag);

									for (int l = 0; l < ((Output) circuit
											.getGate(i + 1)).getNumberWire(); l++)
										if (((Output) circuit.getGate(i + 1))
												.getWire(l) != null)
											((Output) circuit.getGate(i + 1))
													.getWire(l).setNewStart(
															ev.getX(),
															ev.getY());
								}
							}

						}
					}
				}

				repaint();
				mouseXDrag = ev.getX();
				mouseYDrag = ev.getY();

			}

		});

		addMouseListener(new MouseAdapter() {
			public void mouseReleased(final MouseEvent ev) {
				mouseXDrag = ev.getX();
				mouseYDrag = ev.getY();
				onaElement = false;
				if (wireEnable) {

					// this delete the line after release the mouse
					if (circuit.getNumberParts() > 0
							&& circuit.getGate(circuit.getNumberParts() - 1) instanceof Wire) {
						circuit.getpartList().remove(
								circuit.getNumberParts() - 1);
						repaint();
					}

					// this checks if the line is over an input or an output. If
					// it's ok, it repeaint the line
					for (int i = 0; i < circuit.getNumberParts(); i++) {

						if (circuit.getGate(i) instanceof Pin
								&& ((Pin) circuit.getGate(i)).contains(mouseX,
										mouseY)) {
							outputDraw = (Output) circuit.getGate(i);
						}

					}

					for (int i = 0; i < circuit.getNumberParts(); i++) {

						if (circuit.getGate(i) instanceof CommonGate) {

							for (int c = 0; c < ((CommonGate) circuit
									.getGate(i)).getInputs().size(); c++) {

								if (((CommonGate) circuit.getGate(i))
										.getInputs().get(c)
										.contains(ev.getX(), ev.getY())
										&& outputDraw != null
										&& ((CommonGate) circuit.getGate(i))
												.getInputs().get(c).getInput() == null) {

									// create the wire
									circuit.addPart(new Wire(mouseX, mouseY, ev
											.getX(), ev.getY()));

									((Wire) circuit.getGate(circuit
											.getNumberParts() - 1))
											.setIndex(circuit.getNumberParts() - 1);

									// give the wire to the output
									outputDraw
											.setOutput(((Wire) circuit
													.getpartList()
													.get(circuit
															.getNumberParts() - 1)));

									// add the input of the wire
									((Wire) circuit.getGate(circuit
											.getNumberParts() - 1))
											.setInput(outputDraw);
									// set the output of the wire
									((CommonGate) circuit.getGate(i))
											.getInputs()
											.get(c)
											.setInput(
													(Wire) circuit.getGate(circuit
															.getNumberParts() - 1));

									// set the input of the wire (i hope)
									((Wire) circuit.getGate(circuit
											.getNumberParts() - 1))
											.setPin(((CommonGate) circuit
													.getGate(i)).getInputs()
													.get(c));

									repaint();
								}

							}

						}
					}
				}
			}
		});

		addMouseListener(new MouseAdapter() {

			/**
			 * public void addPins(final MouseEvent ev) { ((CommonGate)
			 * circuit.getGate(circuit.getNumberParts() - 1))
			 * .setPos(circuit.getNumberParts() - 1);
			 * 
			 * ((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
			 * .setInput(new Input(ev.getX(), ev.getY(), (CommonGate)
			 * circuit.getpartList().get( circuit.getNumberParts() - 1)));
			 * 
			 * circuit.addPart(new Output(ev.getX(), ev.getY()));
			 * 
			 * ((Output) circuit.getGate(circuit.getNumberParts() - 1))
			 * .setInput((CommonGate) circuit.getGate(circuit .getNumberParts()
			 * - 2)); }
			 **/

			public void mouseClicked(final MouseEvent ev) {
				mouseX = ev.getX();
				mouseY = ev.getY();

			}

			public void mousePressed(final MouseEvent ev) {
				boolean ok = true;
				mouseX = ev.getX();
				mouseY = ev.getY();
				mouseXDrag = ev.getX();
				mouseYDrag = ev.getY();

				for (int i = 0; i < circuit.getNumberParts(); i++) {
					if (circuit.getGate(i) != null
							&& circuit.getGate(i).getClass()
									.getGenericSuperclass() == CommonGate.class
							&& ((CommonGate) circuit.getGate(i)).contains(
									ev.getX(), ev.getY())) {

						for (int t = 0; t < selectedGate.size(); t++) {
							if (selectedGate.get(t).getIndex() == ((CommonGate) circuit
									.getGate(i)).getIndex()) {
								ok = false;
								break;
							}
						}
						
						if (ok) {
							selectedGate.add((CommonGate) circuit.getGate(i));
						}

						try {
							selectedOutput.add((Output) circuit.getGate(i + 1));
						} catch (Exception e) {

						}
						
						if (circuit.getGate(i).getClass() == MegaInput.class) {
							selectedMegaInput = (MegaInput) circuit.getGate(i);
						}

						if (circuit.getGate(i).getClass() == MegaOutput.class) {
							selectedMegaOutput = (MegaOutput) circuit
									.getGate(i);
						}
						
						onaElement = true;
						resetEnable();
					}

				}

				if (!onaElement) {
					selectedGate = new ArrayList<CommonGate>();
					selectedOutput = new ArrayList<Output>();
					selectedMegaOutput = null;
					selectedMegaInput = null;
					refreshDrawer(0);
				}

				if (notEnable) {
					circuit.addPart(new Not(new Timer(), ev.getX(),
							ev.getY() + 5, circuit.getNumberParts()));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setPos(circuit.getNumberParts() - 1);

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 7, ev.getY() - 1,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					circuit.addPart(new Output(ev.getX() + 10, ev.getY()));

					((Output) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput((CommonGate) circuit.getGate(circuit
									.getNumberParts() - 2));

					System.out.println("added a Not");
				}

				if (orEnable) {
					circuit.addPart(new Or(new Timer(), ev.getX(),
							ev.getY() + 5, circuit.getNumberParts()));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setPos(circuit.getNumberParts() - 1);

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 7, ev.getY() - 12,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 7, ev.getY() + 8,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					circuit.addPart(new Output(ev.getX() + 10, ev.getY()));

					((Output) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput((CommonGate) circuit.getGate(circuit
									.getNumberParts() - 2));

					System.out.println("added a Or");
				}

				if (xorEnable) {
					circuit.addPart(new Xor(new Timer(), ev.getX(),
							ev.getY() + 5, circuit.getNumberParts()));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setPos(circuit.getNumberParts() - 1);

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 7, ev.getY() - 12,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 7, ev.getY() + 8,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					circuit.addPart(new Output(ev.getX() + 10, ev.getY()));

					((Output) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput((CommonGate) circuit.getGate(circuit
									.getNumberParts() - 2));

					System.out.println("added a Xor");
				}

				if (nandEnable) {
					circuit.addPart(new Nand(new Timer(), ev.getX(),
							ev.getY() + 5, circuit.getNumberParts()));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setPos(circuit.getNumberParts() - 1);

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 15, ev.getY() - 12,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 15, ev.getY() + 8,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					circuit.addPart(new Output(ev.getX() + 10, ev.getY()));

					((Output) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput((CommonGate) circuit.getGate(circuit
									.getNumberParts() - 2));

					System.out.println("added a Nand");
				}

				if (andEnable) {
					circuit.addPart(new And(new Timer(), ev.getX(),
							ev.getY() + 5, circuit.getNumberParts()));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setPos(circuit.getNumberParts() - 1);

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 17, ev.getY() - 12,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 17, ev.getY() + 8,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					circuit.addPart(new Output(ev.getX(), ev.getY()));

					((Output) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput((CommonGate) circuit.getGate(circuit
									.getNumberParts() - 2));

					System.out.println("added a And");
				}

				if (megaInputEnable) {
					circuit.addPart(new MegaInput(ev.getX(), ev.getY(), circuit
							.getNumberParts()));

					((MegaInput) circuit.getGate(circuit.getNumberParts() - 1))
							.setValue(((WaveForm) FileLoader.load("wave",
									"WaveForm")).getValue(), 800);

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setPos(circuit.getNumberParts() - 1);

					circuit.addPart(new Output(ev.getX() + 10, ev.getY() - 4));

					((Output) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput((CommonGate) circuit.getGate(circuit
									.getNumberParts() - 2));

					System.out.println("added a megaInput");
				}

				if (megaOutputEnable) {
					circuit.addPart(new MegaOutput(ev.getX(), ev.getY(),
							circuit.getNumberParts()));

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setPos(circuit.getNumberParts() - 1);

					((CommonGate) circuit.getGate(circuit.getNumberParts() - 1))
							.setInput(new Input(ev.getX() - 8, ev.getY() - 6,
									(CommonGate) circuit.getpartList().get(
											circuit.getNumberParts() - 1)));

					System.out.println("added a megaOutput");

				}

				if (wireEnable) {
					circuit.addPart(new Wire(0, 0, 0, 0));
				}

				repaint();
			}
		});

	}

	public void giveDrawer(MegaInputDrawer megaInputDrawer,
			MegaOutputDrawer megaOutputDrawer) {
		this.megaInputDrawer = megaInputDrawer;
		this.megaOutputDrawer = megaOutputDrawer;
	}

	public void refreshDrawer(int n) {
		
		megaOutputDrawer.setPos(n);
		megaInputDrawer.setPos(n);
		megaInputDrawer.repaint();
		megaOutputDrawer.repaint();
	}

	public ArrayList<CommonGate> getFocusedGate() {
		return selectedGate;
	}

	public void deleteFocusElement() {
		selectedGate = new ArrayList<CommonGate>();
		selectedOutput = new ArrayList<Output>();
	}

	public void draw() {
	}

	public void setNot() {
		notEnable = true;
	}

	public void setAnd() {
		andEnable = true;
	}

	public void setXor() {
		xorEnable = true;
	}

	public void setNand() {
		nandEnable = true;
	}

	public void setOr() {
		orEnable = true;
	}

	public void setWire() {
		wireEnable = true;
	}

	public void setMegaInput() {
		megaInputEnable = true;
	}

	public void setMegaOutput() {
		megaOutputEnable = true;
	}

	public void setCompile() {
		compileEnable = !compileEnable;
	}

	public boolean getCompile() {
		return compileEnable;
	}

	public void setClocked() {
		clockedEnable = !clockedEnable;
		System.out.println(clockedEnable);
	}

	public boolean getClocked() {
		return clockedEnable;
	}

	public MegaInput getSelectedMegaInput() {
		return selectedMegaInput;
	}

	public MegaOutput getSelectedMegaOutput() {
		return selectedMegaOutput;
	}

	public void resetEnable() {

		wireEnable = false;
		notEnable = false;
		andEnable = false;
		orEnable = false;
		nandEnable = false;
		xorEnable = false;
		megaInputEnable = false;
		megaOutputEnable = false;
		compileEnable = false;
	}

	public void deleteAll() {
		circuit.partList = new ArrayList();
		selectedGate = null;
		repaint();

	}

	// ////////////////////////////////////////////////////////////////////
	public void paintComponent(final Graphics g) {

		g.setColor(Color.WHITE);

		g.fillRect(20, 20, getWidth() - 50, getHeight() - 50);

		g.setColor(Color.BLACK);

		if (!compileEnable) {
			n=0;

			for (int i = 0; i < circuit.getNumberParts(); i++) {
				if (circuit.getGate(i) != null
						&& circuit.getGate(i) instanceof CommonGate) {
					((CommonGate) circuit.getGate(i)).draw(g);

					ArrayList<Input> inputs = ((CommonGate) circuit.getGate(i))
							.getInputs();

					for (int c = 0; c < inputs.size(); c++) {
						inputs.get(c).draw(g);
					}

				}

				if (circuit.getGate(i) != null
						&& circuit.getGate(i).getClass().getGenericSuperclass() == Pin.class) {
					((Pin) circuit.getGate(i)).draw(g);
				}
				if (circuit.getGate(i) != null
						&& circuit.getGate(i).getClass() == Wire.class) {

					((Wire) circuit.getGate(i)).draw(g);
				}
			}

			g.setColor(Color.RED);
			if (selectedGate != null) {
				for (int k = 0; k < selectedGate.size(); k++) {
					selectedGate.get(k).draw(g);

					if (selectedGate.get(k).getInputs() != null) {
						for (int i = 0; i < selectedGate.get(k).getInputs()
								.size(); i++) {
							selectedGate.get(k).getInputs().get(i).draw(g);
						}
						if (selectedOutput != null) {
							for (int y = 0; y < selectedOutput.size(); y++) {
								try{
								selectedOutput.get(y).draw(g);}
								catch(Exception e){}
							}
						}
					}
				}

			}
		}
		if (compileEnable) {

			for (int i = 0; i < circuit.getNumberParts(); i++) {
				if (circuit.getGate(i) != null
						&& circuit.getGate(i).getClass().getGenericSuperclass() == CommonGate.class
						&& ((CommonGate) circuit.getGate(i)).getValue(n) != null
						&& ((CommonGate) circuit.getGate(i)).getValue(n) == 1) {

					g.setColor(Color.GREEN);
					((CommonGate) circuit.getGate(i)).draw(g);

					ArrayList<Input> inputs = ((CommonGate) circuit.getGate(i))
							.getInputs();
					for (int c = 0; c < inputs.size(); c++) {
						inputs.get(c).draw(g);
					}

				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i) instanceof CommonGate
						&& ((CommonGate) circuit.getGate(i)).getValue(n) == null) {

					g.setColor(Color.BLUE);
					((CommonGate) circuit.getGate(i)).draw(g);

					for (int c = 0; c < ((CommonGate) circuit.getGate(i))
							.getInputs().size(); c++) {
						((CommonGate) circuit.getGate(i)).getInputs().get(c)
								.draw(g);
					}
				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i) instanceof CommonGate
						&& ((CommonGate) circuit.getGate(i)).getValue(n) == 0) {

					g.setColor(Color.ORANGE);
					((CommonGate) circuit.getGate(i)).draw(g);

					for (int c = 0; c < ((CommonGate) circuit.getGate(i))
							.getInputs().size(); c++) {
						((CommonGate) circuit.getGate(i)).getInputs().get(c)
								.draw(g);
					}
				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i).getClass().getGenericSuperclass() == Pin.class
						&& ((Pin) circuit.getGate(i)).getValue(n) != null
						&& ((Pin) circuit.getGate(i)).getValue(n) == 1) {
					g.setColor(Color.GREEN);
					((Pin) circuit.getGate(i)).draw(g);

				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i).getClass().getGenericSuperclass() == Pin.class
						&& ((Pin) circuit.getGate(i)).getValue(n) == null) {
					g.setColor(Color.BLUE);
					((Pin) circuit.getGate(i)).draw(g);

				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i).getClass().getGenericSuperclass() == Pin.class
						&& ((Pin) circuit.getGate(i)).getValue(n) == 0) {
					g.setColor(Color.ORANGE);
					((Pin) circuit.getGate(i)).draw(g);

				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i) instanceof Wire
						&& ((Wire) circuit.getGate(i)).getValue(n) != null
						&& ((Wire) circuit.getGate(i)).getValue(n) == 1) {

					g.setColor(Color.GREEN);
					((Wire) circuit.getGate(i)).draw(g);
				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i) instanceof Wire
						&& ((Wire) circuit.getGate(i)).getValue(n) == null) {

					g.setColor(Color.BLUE);
					((Wire) circuit.getGate(i)).draw(g);
				}

				else if (circuit.getGate(i) != null
						&& circuit.getGate(i) instanceof Wire
						&& ((Wire) circuit.getGate(i)).getValue(n) == 0) {

					g.setColor(Color.ORANGE);
					((Wire) circuit.getGate(i)).draw(g);

				}

			}
			
			circuit.getExit(n);
			n++;
			
			new Thread(new Runnable() {
				 public void run() { refreshDrawer(n);}}).start();
			
			try {
				Thread.sleep(100);
			} catch (Exception e) {
			}

		}
	
		repaint();

	}

	public ArrayList<CommonGate> getSelectedGate() {
		return selectedGate;
	}

	public void setSelectedGate(ArrayList<Object> selectedGate) {
		this.selectedGate = new ArrayList<CommonGate>();
		this.selectedOutput = new ArrayList<Output>();
		for (int i = 0; i < selectedGate.size(); i++) {
			if (selectedGate.get(i) instanceof CommonGate) {
				this.selectedGate.add((CommonGate) selectedGate.get(i));
			}
			if (selectedGate.get(i) instanceof Output) {
				this.selectedOutput.add((Output) selectedGate.get(i));
			}

		}
	}

	public void setCommonGateFinder(CommonGate commonGateFinder) {
		this.commonGateFinder = commonGateFinder;
	}

	public CommonGate getCommonGateFinder() {
		return commonGateFinder;
	}

}
